package de.mmis.utilities.consoleWrapper;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.swing.SwingUtilities;

public class ConsoleWrapperMain {

	private static void printHelp() {
		System.out
				.println("Usage: java -jar ConsoleWrapper.jar [Options] (-c Class | Application.jar) [-- application arguments]\n");
		System.out.println("Options:");
		System.out
				.println("-n capacity    Limit number of characters of output saved, in KB, -1 means no limit. Default: 100");
		System.out.println("-e          Suppress local echo");
		System.out.println("-l prefix   log output to file prefix_Timestamp");
		System.out
				.println("-c Class    Start main Method of the specified class");

		System.out
				.println("\nEvery argument after -- is passed directly to the application");
	}

	private static Class<?> getClassFromJar(File Jar) {
		JarFile jarFile;
		try {
			jarFile = new JarFile(Jar);
		} catch (IOException e1) {
			System.err.println("Unable to open JAR " + Jar.toString());
			return null;
		}

		Manifest manifest;
		try {
			manifest = jarFile.getManifest();
		} catch (IOException e1) {
			System.err.println("Unable to open Manifest");
			return null;
		}

		String mainClass = manifest.getMainAttributes().getValue("Main-Class");
		if (mainClass == null) {
			System.err.println("Main-Class not defined in the Manifest");
			return null;
		}

		URL url;
		try {
			url = Jar.toURI().toURL();
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
			return null;
		}

		URLClassLoader cl = new URLClassLoader(new URL[] { url });
		Class<?> c;
		try {
			c = cl.loadClass(mainClass);
		} catch (ClassNotFoundException e1) {
			System.err.println("Class not found: " + e1);
			return null;
		}

		return c;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// first show window, then parse arguments etc.
		// just because it's likely no one will see the console output
		final ConsoleWrapper wrapper = ConsoleWrapper
				.createInstance(true, null);

		String mainClass = null;
		boolean localEcho = true;
		int capacity = 100;
		String logPrefix = null;
		File Jar = null;
		String[] applicationArgs = new String[0];

		int i = 0;
		while (i < args.length) {
			if (args[i].equals("--")) {
				applicationArgs = Arrays.copyOfRange(args, i + 1, args.length);
				break;
			} else if (args[i].equals("--help")) {
				printHelp();
				return;
			} else if (args[i].equals("-n")) {
				try {
					i++;
					if (i >= args.length) {
						System.err.println("Please specify a number");
					}
					capacity = Integer.parseInt(args[i]);
				} catch (NumberFormatException _) {
					System.err.println(args[i] + " is no valid number");
				}
			} else if (args[i].equals("-e"))
				localEcho = false;
			else if (args[i].equals("-l"))
				logPrefix = args[++i];
			else if (args[i].equals("-c"))
				mainClass = args[++i];
			else
				Jar = new File(args[i]);
			i++;
		}

		if (Jar == null && mainClass == null) {
			System.err.println("Please specify an application or class");
			return;
		}

		if (Jar != null && mainClass != null) {
			System.err
					.println("Specifying an application and class is not permitted.");
		}

		wrapper.setLocalEcho(localEcho);
		wrapper.setCapacity(capacity * 1024);
		try {
			wrapper.setLogPrefix(logPrefix);
		} catch (IOException e1) {
			System.err.println("Could not create log file");
			e1.printStackTrace();
		}

		final Class<?> c;
		if (Jar != null) {
			c = getClassFromJar(Jar);
			if (c == null)
				return;
		} else
			try {
				c = Class.forName(mainClass);
			} catch (ClassNotFoundException e1) {
				System.err.println("Class " + mainClass + " not found");
				return;
			}

		if (c.equals(ConsoleWrapper.class)) {
			System.err.println("Rejected to wrap around myself");
			return;
		}

		// set window title to name of the package
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				Package pack = c.getPackage();
				if (pack == null) {
					wrapper.setTitle(c.getName());
					return;
				}

				String packName = pack.getName();
				String title = packName.substring(packName.lastIndexOf('.') + 1);
				wrapper.setTitle(title);
			}
		});

		Method main = null;
		for (Method m : c.getMethods()) {
			// check name, modifiers and return type
			if (m.getName().equals("main")
					&& Modifier.isStatic(m.getModifiers())
					&& Modifier.isPublic(m.getModifiers())
					&& m.getReturnType().equals(void.class)) {
				// check parameters
				Class<?>[] parameterTypes = m.getParameterTypes();
				if (parameterTypes.length == 1
						&& parameterTypes[0].equals(String[].class)) {
					main = m;
					break;
				}
			}
		}

		if (main == null) {
			System.err.println("No main-Method found");
			return;
		}

		try {
			main.invoke(null, new Object[] { applicationArgs });
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			return;
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			return;
		} catch (InvocationTargetException e) {
			System.err.println("Program has thrown an Exception: ");
			e.printStackTrace();
		}

		System.err.println("\n\nProgram terminated");
	}

}
